﻿using System;
using RpcClient.Config;
using RpcClient.Helper;
using RpcClient.Interface;
using RpcClient.Track;
using RpcClient.Track.Config;
using RpcClient.Track.Model;
using RpcModel.Model;

namespace RpcClient.Collect
{
    [Attr.ClassLifetimeAttr(Attr.ClassLifetimeType.单例)]
    internal class TrackCollect : ITrackCollect
    {
        /// <summary>
        /// 跨度
        /// </summary>
        [ThreadStatic]
        private static TrackSpan _Span = null;
        /// <summary>
        /// 当前跨度
        /// </summary>
        [ThreadStatic]
        private static TrackSpan _CurrentSpan = null;
        /// <summary>
        /// 跨度ID
        /// </summary>
        public string TraceId => _Span?.ToTraceId();
        /// <summary>
        /// 链路配置
        /// </summary>
        private static TrackConfig _Config = new TrackConfig();
        /// <summary>
        /// 采样器
        /// </summary>
        private static ISampler _Sampler = null;

        private static ITack _Tack = null;

        /// <summary>
        /// 是否启用
        /// </summary>
        public static bool IsEnable => _Config.IsEnable;

        public TrackConfig Config => _Config;

        public static TrackSpan CurrentSpan => _CurrentSpan;

        public TrackSpan TrackSpan => _CurrentSpan;

        public static event Action<TrackConfig> EnableChange;
        /// <summary>
        /// 检查是否采样
        /// </summary>
        /// <param name="range"></param>
        /// <param name="spanId"></param>
        /// <returns></returns>
        public bool CheckIsTrack(TrackRange range, out long spanId)
        {
            if (_Span != null)
            {
                spanId = _ApplySpanId();
                return true;
            }
            else if (!_Config.IsEnable || (range & _Config.TrackRange) != range)
            {
                spanId = 0;
                return false;
            }
            return _Sampler.Sample(out spanId);
        }

        

        static TrackCollect()
        {
            RpcClient.Config.AddRefreshEvent((a, name) =>
            {
                if (name == "rpcassembly:Track" || name.StartsWith("rpcassembly:Track:"))
                {
                    Init();
                }
            });
            Init();
        }


        /// <summary>
        /// 初始化链路追踪
        /// </summary>
        public static void Init()
        {
            if (!RpcStateCollect.IsInit)
            {
                return;
            }
            TrackConfig config = WebConfig.GetTrackConfig();
            if (config.IsEnable && config.IsNull())
            {
                config.IsEnable = false;
            }
            if (_Sampler == null || config.SamplingRate != _Config.SamplingRate)
            {
                _Sampler = new DefaultSampler(_ApplySpanId(), config.SamplingRate);
            }
            if (config.IsEnable)
            {
                if (_Tack == null)
                {
                    _InitTrack(config);
                }
                else if (!_Config.IsEquals(config))
                {
                    _Tack.Dispose();
                    _InitTrack(config);
                }
            }
            bool isChange = config.IsEnable != _Config.IsEnable;
            _Config = config;
            if (isChange && EnableChange != null)
            {
                EnableChange(config);
            }
        }
        private static void _InitTrack(TrackConfig config)
        {
            if (config.TraceType == TraceType.Zipkin)
            {
                _Tack = new ZipkinTack(config.ZipkinTack, config.ServiceName);
            }
            else
            {
                _Tack = new LocalTack(config.Local, config.ServiceName);
            }
        }
        internal static void ClearTrack(TrackSpan span)
        {
            if (span.ParentId.HasValue && span.SpanId != _Span?.SpanId)
            {
                _CurrentSpan = null;
            }
            else
            {
                _Span = null;
                _CurrentSpan = null;
            }
        }
        private TrackSpan _CreateTrack(long spanId)
        {
            return new TrackSpan
            {
                TraceId = _ApplySpanId(),
                HighTraceId = _Config.Trace128Bits ? _ApplySpanId() : 0,
                SpanId = spanId
            };
        }
        private TrackSpan _CreateTrack(TrackSpan span, long spanId)
        {
            return new TrackSpan
            {
                TraceId = span.TraceId,
                HighTraceId = span.HighTraceId,
                SpanId = spanId,
                ParentId = span.SpanId
            };
        }
        public TrackSpan CreateTrack(long spanId)
        {
            if (_Span == null)
            {
                TrackSpan span = this._CreateTrack(spanId);
                _Span = span;
                _CurrentSpan = span;
                return span;
            }
            _CurrentSpan = this._CreateTrack(_Span, spanId);
            return _CurrentSpan;
        }

        public void EndTrack(TrackBody track)
        {
            DateTime now = DateTime.Now;
            track.Annotations[1].Time = now;
            track.Duration = (int)((now - track.Time).TotalMilliseconds * 1000L);
            _Tack.AddTrace(track);
        }

        public TrackSpan CreateAnswerTrack(TrackSpan span)
        {
            TrackSpan track = span == null ? this._CreateTrack(_ApplySpanId()) : this._CreateTrack(span, _ApplySpanId());
            this.SetTrack(track);
            return track;
        }
        /// <summary>
        /// 获取发起的模板
        /// </summary>
        /// <returns></returns>
        public TrackDepth[] GetArgTemplate()
        {
            return _Config.GetArgTemplate();
        }
        public TrackDepth[] GetAnswerTemplate()
        {
            return _Config.GetAnswerTemplate();
        }

        public void SetTrack(TrackSpan track)
        {
            _Span = track;
            _CurrentSpan = track;
        }

        public long ApplySpanId()
        {
            return _ApplySpanId();
        }
        private static long _ApplySpanId()
        {
            return RandomUtils.NextLong();
        }
    }
}
